package com.ysstech.valuation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ysstech.common.entity.PageBean;
import com.ysstech.common.util.BigDecimalUtil;
import com.ysstech.common.util.DateUtil;
import com.ysstech.valuation.entity.BondDealDetailed;
import com.ysstech.valuation.entity.BondInfo;
import com.ysstech.valuation.mapper.BondDealDetailedMapper;
import com.ysstech.valuation.service.BondDealDetailedService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ysstech.valuation.service.BondInfoService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lishuangliang
 * @since 2022-04-19
 */
@Service
public class BondDealDetailedServiceImpl extends ServiceImpl<BondDealDetailedMapper, BondDealDetailed> implements BondDealDetailedService {

    @Autowired
    private BondInfoService bondInfoService;

    /**
     * 查询债券交易流水信息
     *
     * @param bondDealDetailed
     * @param pageBean
     * @return
     * @throws Exception
     */
    public PageBean<BondDealDetailed> queryAllBondDealDetailed(BondDealDetailed bondDealDetailed, PageBean pageBean) throws Exception {
        QueryWrapper<BondDealDetailed> wrapper = this.getQueryWrapper(bondDealDetailed);
        IPage pageBalance = this.page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()), wrapper);
        this.switchedListBondInfo(pageBalance.getRecords());
        return new PageBean<>(pageBalance);
    }

    /**
     * 类型，机构等中文的转换
     *
     * @param list
     * @return
     * @throws Exception
     */
    public List<BondDealDetailed> switchedListBondInfo(List<BondDealDetailed> list) throws Exception {
        for (BondDealDetailed bondDealDetailed : list) {
            if (null == bondDealDetailed) {
                continue;
            }
            this.updateBondDealDetailed(bondDealDetailed);
        }
        return list;
    }

    /**
     * 查询条件
     *
     * @param bondDealDetailed
     * @return
     * @throws Exception
     */
    private QueryWrapper<BondDealDetailed> getQueryWrapper(BondDealDetailed bondDealDetailed) throws Exception {
        QueryWrapper<BondDealDetailed> wrapper = new QueryWrapper<>();
        if (null == bondDealDetailed) {
            return wrapper;
        }
        if (StringUtils.isNotBlank(bondDealDetailed.getBondcode())) {
            wrapper.like("BONDCODE", bondDealDetailed.getBondcode());
        }
        return wrapper;
    }

    /**
     * 更新债券交易流水信息
     *
     * @throws Exception
     */
    private void updateBondDealDetailed(BondDealDetailed bondDealDetailed) throws Exception {
        if (null == bondDealDetailed || StringUtils.isBlank(bondDealDetailed.getBondcode())) {
            return;
        }
        QueryWrapper<BondInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("SECURITY_CODE", bondDealDetailed.getBondcode());
        BondInfo bondInfo = bondInfoService.getOne(wrapper);
        if (null == bondInfo) {
            return;
        }
        // 业务日期
        Date fdate = DateUtil.strToDate(DateUtil.localDateToStr(bondDealDetailed.getFdate()));
        // 债券的起息日
        Date startDate = DateUtil.strToDate(DateUtil.localDateToStr(bondInfo.getStartBearingData()));
        for (int i = 1; i <= 100; i++) {
            startDate = DateUtil.addMonth(startDate, 6);
            Date endDate = DateUtil.addMonth(startDate, 6);
            if (DateUtil.differentDaysByDate(fdate, startDate) <= 0 && DateUtil.differentDaysByDate(fdate, endDate) >= 0) {
                bondInfo.setThisStartBearingData(startDate);
                bondInfo.setThisEndBearingData(endDate);
                System.out.println(DateUtil.dateToStr(startDate));
                System.out.println(DateUtil.dateToStr(endDate));
                break;
            }
        }
        // 获取债券溢折价
        BigDecimal bondExcessDiscount = this.getBondExcessDiscount(bondInfo, bondDealDetailed);
        bondDealDetailed.setExcessDiscount(bondExcessDiscount);
        // 获取债券利息
        BigDecimal bondInterest = this.getBondInterest(bondInfo, bondDealDetailed);
        bondDealDetailed.setInterest(bondInterest);
        // 获取债券实际收益率
        BigDecimal BondRealIncome = this.getBondRealIncome(bondInfo, bondDealDetailed);
        bondDealDetailed.setRealIncome(BondRealIncome);
        this.updateById(bondDealDetailed);
    }

    /**
     * 更新债券的实际收益率
     *
     * @param bondInfo         债券基本信息
     * @param bondDealDetailed 债券交易流水
     * @return
     * @throws Exception
     */
    public BigDecimal getBondRealIncome(BondInfo bondInfo, BondDealDetailed bondDealDetailed) throws Exception {
        try {
            int i = 0;
            // 业务日期
            String fdate = DateUtil.localDateToStr(bondDealDetailed.getFdate());
            // 初始溢折价 溢折价科目余额/库存数量-融出数量
            BigDecimal dubYzj = BigDecimalUtil.divide(bondDealDetailed.getExcessDiscount(), bondDealDetailed.getDealAmount(), 18);
            BigDecimal dWc = new BigDecimal("-10").pow(15).setScale(18);
            bondInfo.setFaceRate(BigDecimalUtil.divide(bondInfo.getFaceRate(), new BigDecimal("100"))); // 获取票面利率
            LocalDate startBearingData = bondInfo.getStartBearingData();// 计息截起始日
            LocalDate endBearingData = bondInfo.getEndBearingData();// 计息截止日
            // 业务日期和结束日期之前闰年数
            int leap = DateUtil.getLeapYears(DateUtil.strToDate(fdate), DateUtil.strToDate(DateUtil.localDateToStr(endBearingData)));
            String qjjzr = DateUtil.addYear(fdate, 1);
            // 一个计息区间的时间天数，按年付息就是年实际天数
            int realDays = (int) (DateUtil.strToLocalDate(qjjzr).toEpochDay() - DateUtil.strToLocalDate(fdate).toEpochDay()) - leap;
            // 这个计息区间公共天数
            int lastDayInt = (int) (endBearingData.toEpochDay() - startBearingData.toEpochDay()) - leap;
            BigDecimal dsubIncome = BigDecimal.ZERO;
            // 最大收益率
            BigDecimal dblMax = BigDecimalUtil.divide(new BigDecimal(4), new BigDecimal(365), 18);
            // 最小收益率
            BigDecimal dblMin = BigDecimalUtil.divide(new BigDecimal(-1), new BigDecimal(365), 18);
            do {
                // 最大最小收益率平均值
                BigDecimal dubRetu = BigDecimalUtil.divide(BigDecimalUtil.add(dblMax, dblMin), new BigDecimal(2), 18);
                // 收益率
                dsubIncome = BigDecimalUtil.multiply(dubYzj, BigDecimalUtil.add(new BigDecimal(1), dubRetu).pow(lastDayInt));
                // 票面日利率 （整个存续期票面日利率不变动）
                BigDecimal faceRateDays = BigDecimalUtil.divide(bondInfo.getFaceRate(), new BigDecimal(realDays), 18);
                //收益率 +票面金额* （收益率平均值-票面日利率） * pow(（1+收益率平均值）,剩余天数) /收益率平均值
                dsubIncome = BigDecimalUtil.add(dsubIncome,
                        BigDecimalUtil.multiply(bondInfo.getFaceMoney(), BigDecimalUtil.subtract(dubRetu, faceRateDays),
                                BigDecimalUtil.divide(BigDecimalUtil.add(new BigDecimal(1), dubRetu).pow(lastDayInt), dubRetu, 18)));
                // 收益率-票面金额*（收益率平均值-票面日利率）*(1/收益率平均值)
                dsubIncome = BigDecimalUtil.subtract(dsubIncome,
                        BigDecimalUtil.multiply(bondInfo.getFaceMoney(), BigDecimalUtil.subtract(dubRetu, faceRateDays),
                                BigDecimalUtil.divide(new BigDecimal("1"), dubRetu, 18)));

                if (BigDecimalUtil.compareTo(dsubIncome, BigDecimal.ZERO) > 0) {
                    dblMax = BigDecimalUtil.divide(BigDecimalUtil.add(dblMax, dblMin), new BigDecimal(2), 18);
                } else {
                    dblMin = BigDecimalUtil.divide(BigDecimalUtil.add(dblMax, dblMin), new BigDecimal(2), 18);
                }
                ++i;
                if (i >= 40) {
                    return dubRetu;
                }
            } while (BigDecimalUtil.compareTo(dsubIncome.abs(), dWc) > 0);
            return dsubIncome;
        } catch (Exception ex) {
            log.error("计算债券的实际收益率失败" + ex.toString());
            throw new Exception(ex);
        }
    }


    /**
     * 更新债券的利息
     *
     * @param bondInfo         债券基本信息
     * @param bondDealDetailed 债券交易流水
     * @return
     * @throws Exception
     */
    public BigDecimal getBondInterest(BondInfo bondInfo, BondDealDetailed bondDealDetailed) throws Exception {
        try {
            // 业务日期
            String fdate = DateUtil.localDateToStr(bondDealDetailed.getFdate());
            // 计息截起始日
            Date thisStartBearingData = bondInfo.getThisStartBearingData();
            // 已计息天数
            int realDays = DateUtil.differentDaysByString(fdate, DateUtil.dateToStr(bondInfo.getThisEndBearingData()));
            // 当前计息区间总天数
            int lastDayInt = DateUtil.differentDaysByDate(thisStartBearingData, bondInfo.getThisEndBearingData());
            // 票面利息/当前计息区间天数*已计息天数*票面金额/100*成交数量*10
            BigDecimal bondInterest = BigDecimalUtil.multiply(BigDecimalUtil.divide(bondInfo.getFaceRate(), new BigDecimal(lastDayInt), 18),
                    new BigDecimal(realDays));
            bondInterest = BigDecimalUtil.divide(BigDecimalUtil.multiply(bondInterest, bondInfo.getFaceMoney()), new BigDecimal("100"));
            bondInterest = BigDecimalUtil.multiply(bondInterest, bondDealDetailed.getDealAmount(), 10);
            return bondInterest;
        } catch (Exception ex) {
            log.error("计算债券的利息失败" + ex.toString());
            throw new Exception(ex);
        }
    }


    /**
     * 更新债券溢折价
     *
     * @param bondDealDetailed 债券流水信息
     * @param bondInfo         债券基本信息
     * @return
     * @throws Exception
     */
    public BigDecimal getBondExcessDiscount(BondInfo bondInfo, BondDealDetailed bondDealDetailed) throws Exception {
        try {
            // 如果买入 成交金额+利息+佣金+费用 －债券面额×数量－应收债券投资利息
            BigDecimal excessDiscount = BigDecimalUtil.add(bondDealDetailed.getDealMoney(), bondDealDetailed.getCommissionFee());
            excessDiscount = BigDecimalUtil.subtract(excessDiscount, BigDecimalUtil.multiply(bondInfo.getFaceMoney(), bondDealDetailed.getDealAmount()));
            return excessDiscount;
        } catch (Exception ex) {
            log.error("计算债券的溢折价失败" + ex.toString());
            throw new Exception(ex);
        }
    }

}
